import { Component, OnInit, ElementRef, ViewChild, ViewEncapsulation, EventEmitter, OnChanges } from '@angular/core';
import { getId, StockKData, StockDetail, getStockTypeThreeOld } from "utils";
import { CmHttp } from "CmHttp";
import { Observable } from "rxjs/Observable";
import { Action, State } from "@ngrx/store";
import { SocketService, BROADCAST_EVENT } from "socket";
import { SummaryAssistantService, SummaryWithTrading, SUMMARY_STATUS, STOCK_STATUS } from "summary-assistant";
import { BroadcastService, BROADCAST_TYPE } from "broadcast";
import { Subscription } from "rxjs/Subscription";
import { STOCKURL } from "shared";
import * as _ from "lodash";
import * as moment from "moment";
import { StockTopBarService, ACTION_GO_NEXT, ACTION_GO_PRE } from "stock";
import { compare } from "../../lib/tschart/src/lib/index"

import RenderTsChart from "./../../lib/tschart/src/ts.chart.js";

interface TsChartParam {
  id: string;
  data: any[];
  period: number;
  priceMid: number;
}

export const SET_STOCK = "SET_STOCK";
export const SET_DURATION = "SET_DURATION";
export const SET_PERIOD = "SET_PERIOD";
export const LINES_PER_DAY = 300;

const TS_CHART_KEY = {
  "ArrowUp": "ArrowUp",
  "ArrowDown": "ArrowDown"
};

const TS_TYPE_NORMAL = 0; // 普通数据
const TS_TYPE_WITH_EMPTY = 1; // 为当日添加空数组，主要用于集合竞价阶段
const TS_TYPE_WITH_TAIL = 2; // 为当日添加前一交易日模拟数据，主要用于交易日停牌阶段股票
const GROUPED_DATE_FORMAT = "YYYYMMDD";
const replaceToToday = time => moment(time).format("YYYY-MM-DD HH:mm:ss").replace(/\d{4}-\d{2}-\d{2}(?=\s)/, moment().format("YYYY-MM-DD"))
const getGroupedTsData = (rawData: StockKData[], duration, tsType) => {
  // console.debug("Ts Raw Data: ", rawData);
  // 按日期数列分组
  let grouped = _.groupBy(rawData, e => moment(e.time).format(GROUPED_DATE_FORMAT));
  // console.debug("Ts Raw Data: Grouped", grouped);
  // { 20080202: [ts,ts,ts], 20080303: [ts,ts,ts]}
  let today = moment().format(GROUPED_DATE_FORMAT);
  switch (tsType) {
    case TS_TYPE_NORMAL:
      break;
    case TS_TYPE_WITH_EMPTY:
      // 如果还未有今天数据，补充空数列给今天
      if (grouped[today] === undefined) {
        grouped[today] = [];
      }
      break;
    case TS_TYPE_WITH_TAIL:
      if (grouped[today] === undefined) {
        grouped[today] =
          // 按日期排列数据，并取最后一天数据数组
          grouped[Object.keys(grouped).sort().slice(-1)[0]]
            // 映射最后一天的数据，其中每一数据集均替换为最后一天最后一个数据，仅保留时间
            .map((kData, i, arr) => Object.assign({}, arr.slice(-1)[0], { time: replaceToToday(kData.time) }))
      }
      break;
  }
  // 依据时间排序，取当前周期，并加一天的数据为最终数据
  return Object.keys(grouped).sort().map(date => grouped[date]).slice(-1 * (duration + 1));
}

@Component({
  // moduleId: module.id,
  selector: 'quote-chart',
  templateUrl: 'quote-chart.component.html',
  encapsulation: ViewEncapsulation.None,
  styleUrls: [
    'quote-chart.component.less'
  ],
  inputs: [
    'updateEventSource',
    'withCaption',
    'duration',
    'hideName'
  ]
})
export class QuoteChartComponent implements OnInit, OnChanges {
  @ViewChild("chart")
  chartContainer: HTMLElement;
  chartInstance: RenderTsChart;
  updateEventSource: Observable<StockDetail>;
  chartId = getId();
  chartSubscription: Subscription;
  stock;
  hideName: boolean;
  stockName = "";
  state;
  currentData;
  kDataList; // 原始的分时图数据，每次初次取回数据，Socket取回数据，均更新此属性，并将此属性传递给groupData方法
  summary; // 股票summary
  storeSubject = new EventEmitter();
  store = this.storeSubject.scan((state, action: Action) => {
    switch (action.type) {
      case SET_STOCK:
        return Object.assign({}, state, { stock: action.payload });
      case SET_DURATION:
        return Object.assign({}, state, {
          duration: action.payload,
          points: action.payload * LINES_PER_DAY
        });
      default: return state;
    }
  }, {
      stock: null,
      duration: 1,
      points: LINES_PER_DAY
    });

  constructor(
    private broadcast: BroadcastService,
    private http: CmHttp,
    private elem: ElementRef,
    private socket: SocketService,
    private summarySrvice: SummaryAssistantService,
    private topBar: StockTopBarService) {
  }

  ngOnChanges(changes) {
    // console.debug("tsChart On Change: ", changes);
    if (changes.duration) {
      this.storeSubject.next({
        type: SET_DURATION,
        payload: changes.duration.currentValue
      });
    }
  }

  ngOnInit() {
    let { chartId, updateEventSource, http, summarySrvice, socket } = this;
    let that = this;
    this.chartSubscription = (
      this.store.distinctUntilChanged((pre, next) => {
        let flag = pre.stock.symbol !== next.stock.symbol || pre.duration !== next.duration;
        if (flag) {
          this.socket.emit("unsub", {
            interval: "timeseries",
            symbol: pre.stock.symbol
          });
        }
        return !flag;
      }).subscribe((state) => {
        this.stock = state.stock;
        this.socket.emit("sub", {
          interval: "timeseries",
          symbol: state.stock.symbol
        });
      })
    );
    this.chartSubscription.add(
      this.store.
        filter(state => {
            return state.stock && state.stock.symbol
          }).
        switchMap(state => {
          // 分时图切换股票loading
          let kchart = document.getElementById(chartId);
          if (kchart) {
            let svg = kchart.getElementsByTagName("svg");
            if (svg[0]) {
              svg[0].innerHTML = "<image class='loading' xmlns:xlink='http://www.w3.org/1999/xlink' xlink:href='/static/images/loading.gif' x='"
                + (svg[0].getAttribute('width') / 2 - 16)
                + "' y='"
                + (svg[0].getAttribute('height') / 2 - 16)
                + "' width='32' height='32'></image>";
            }
          }
       
          return Observable.zip(
            //更换为预加载版本分时获取
            http.getTrend(state.stock.symbol, state.points),
            summarySrvice.getSummaryBySymbol(state.stock.symbol),
            (kDataList: StockKData[], summary) => {
              if (!kDataList.data) return [];
              this.kDataList =  kDataList.data.sort(compare('time'));
              this.summary = summary;
              this.stockName = summary.name;
              this.state = state;
              return this.groupData(kDataList.data, summary, state);
            }
          )
        }).subscribe(wrappedData => {
          if (wrappedData.length == 0) {
            console.error("trend no data");
            return false;
          }
          this.currentData = wrappedData.data;
          // 临时优化：若数据直接从缓存加载，页面元素还没有完成初始化，则需要延后画图时机
          if (document.getElementById(chartId) {
            this.chartInstance = new RenderTsChart({
              ...wrappedData,
              id: chartId,
              title: this.summary["name"],
              period: 1,
              theme: 0
            }, () => null)
          } else {
            let that = this;
            setTimeout(function(){
              that.chartInstance = new RenderTsChart({
                ...wrappedData,
                id: chartId,
                title: that.summary["name"],
                period: 1,
                theme: 0
              }, () => null);
            });
          }
          // 点击指标时绑定k线图焦点
          let chartSvg = document.querySelectorAll(".k-chart") as NodeListOf<HTMLElement>;
          if (chartSvg[0]) {
            chartSvg[0].click();
          }
          console.debug("QuoteChart Instance: ", this, this.chartInstance);
        })
    );
    this.socket.on("TimeSeries", data => {
      console.debug("TimeSeries Socket: ", data, this);
      if (!this.stock) return;
      let tsData = Object.keys(data).map(key => data[key]).reduce(first => first);
      if (tsData.symbol === this.stock.symbol) {
        that.currentData = that.updateKData(that.currentData, tsData);
        that.chartInstance.refreshChart(that.currentData);
      }
    });
    let timer = socket.broadcast.filter(e =>
      e.event === BROADCAST_EVENT.RESET
      && e.symbol === this.stock.symbol
    );
    this.chartSubscription.add(
      Observable.merge(timer, this.updateEventSource).scan((acc, value) => value["symbol"] ? value : acc)
        .subscribe(stock => {
          this.storeSubject.next({
            type: SET_STOCK,
            payload: stock
          });
          console.debug("TimeSeries: ", stock)
        }));
    this.chartSubscription.add(this.broadcast.broadcast
      .filter(e => e.type === BROADCAST_TYPE.RENDER_CHAERT)
      .subscribe(e => this.rerenderChart())
    );
  }

  ngOnDestroy() {
    // console.debug("Unsub Ts because destory: ", this.stock.symbol);
    if (this.stock && this.stock.symbol) {
      this.socket.emit("unsub", {
        interval: "timeseries",
        symbol: this.stock.symbol
      });
    }
    if (this.chartSubscription) {
      this.chartSubscription.unsubscribe();
    }
  }

  groupData(kDataList, summary, state) {
    let groupedData;
    switch (summary.stockStatus) {
      case STOCK_STATUS.NORMAL:
        groupedData = getGroupedTsData(kDataList, state.duration, TS_TYPE_NORMAL);
        break;
      case STOCK_STATUS.OCALL:
        groupedData = getGroupedTsData(kDataList, state.duration, TS_TYPE_WITH_EMPTY);
        break;
      case STOCK_STATUS.STOP_AND_TRADING_DAY:
        groupedData = getGroupedTsData(kDataList, state.duration, TS_TYPE_WITH_TAIL);
        break;
      default:
        groupedData = kDataList;
        break;
    }
    console.debug("TsChart: Grouped Data: ", groupedData);
    let firstDay = groupedData[0];
    if (groupedData.length === 1) {
      return {
        priceMid: firstDay && firstDay.length ? firstDay[0]["open"] || 0 : 0,
        data: groupedData[0],
        stockType: getStockTypeThreeOld(summary)
      };
    }
    return {
      // 现有周期+1天数据（5天则6组），取第一个数据的在最后一位收盘价为价格中数，其余为绘图所需数据
      priceMid: groupedData[0] ? groupedData[0].slice(-1)[0]["close"] : 0,
      data: [..._.flatten(groupedData.splice(1))],
      stockType: getStockTypeThreeOld(summary)
    };
  }

  rerenderChart() {
    // console.debug("QuoteChart: ", this.chartId, this.chartInstance);
    if (!this.chartInstance) return;
    // console.warn("Refresh Quote Chart: ", this.chartId, this);
    this.chartInstance.refreshChart(undefined);
  }

  updateKData(originData, append) {
    if (!originData || !Array.isArray(originData)) return originData;
    if (!originData.slice(-1)[0]) return originData;
    if (originData.slice(-1)[0]["time"] !== append["time"]) {
      return [...originData, append];
    } else {
      return [...originData.slice(0, -1), append];
    }
  }

  handleKeydown(e) {
    switch (e.key) {
      case TS_CHART_KEY.ArrowUp:
        this.topBar.source.next({
          type: ACTION_GO_PRE
        });
        break;
      case TS_CHART_KEY.ArrowDown:
        this.topBar.source.next({
          type: ACTION_GO_NEXT
        });
        break;
    }
  }
}